package cn.genmer.test.security;

import org.locationtech.proj4j.*;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PolygonAreaCalculatorV2 {
    private static final CRSFactory crsFactory = new CRSFactory();


    // V2
    // 2736527 - 2752168.92
    // 952863299 - 955794358.70
    public static void main(String[] args) {
        String input = "[\n" +
                "      [116.403322, 39.920255],\n" +
                "      [116.410703, 39.897555],\n" +
                "      [116.402292, 39.892353],\n" +
                "      [116.389846, 39.891365]\n" +
                "    ]";
        try {
            double area = calculatePolygonArea(input);
            System.out.printf("优化后面积：%.2f 平方米\n", area);
        } catch (IllegalArgumentException e) {
            System.err.println("计算错误: " + e.getMessage());
        }
    }

    public static double calculatePolygonArea(String input) {
        List<Coordinate> coordinates = parseCoordinates(input);
        validateCoordinates(coordinates);
        
        // 动态分带补偿策略
        Coordinate refPoint = coordinates.get(0);
        int mainZone = calculateUTMZone(refPoint.lon);
        String[] candidateCRSCodes = {
            buildCRSCode(mainZone, refPoint.lat),
            buildCRSCode(mainZone + 1, refPoint.lat),
            buildCRSCode(mainZone - 1, refPoint.lat)
        };

        double bestArea = 0.0;
        double minDiff = Double.MAX_VALUE;
        
        for (String crs : candidateCRSCodes) {
            try {
                List<Point> points = convertToUTM(coordinates, crs);
                double area = kahanAreaCalculation(points);
                // 通过坐标范围估算分带误差
                double deviation = estimateZoneDeviation(coordinates, crs); 
                if (deviation < minDiff) {
                    minDiff = deviation;
                    bestArea = area;
                }
            } catch (Exception ignored) {}
        }
        
        return bestArea;
    }

    // 高精度坐标解析（支持科学计数法）
    private static List<Coordinate> parseCoordinates(String input) {
        List<Coordinate> coordinates = new ArrayList<>();
        Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?([Ee][+-]?\\d+)?");
        Matcher matcher = pattern.matcher(input.replaceAll("\\s+", ""));
        
        List<String> numbers = new ArrayList<>();
        while (matcher.find()) numbers.add(matcher.group());

        if (numbers.size() % 2 != 0) 
            throw new IllegalArgumentException("坐标数量应为偶数");

        try {
            for (int i = 0; i < numbers.size(); i += 2) {
                double lon = parseScientificNotation(numbers.get(i));
                double lat = parseScientificNotation(numbers.get(i+1));
                coordinates.add(new Coordinate(lon, lat));
            }
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("坐标格式错误: " + e.getMessage());
        }
        return coordinates;
    }

    private static double parseScientificNotation(String s) {
        return Double.parseDouble(s.replaceAll("[eE]", "e"));
    }

    // Kahan算法改进面积计算
    private static double kahanAreaCalculation(List<Point> points) {
        enforcePolygonClosure(points);
        
        double sum = 0.0;
        double compensation = 0.0;
        int n = points.size();
        
        for (int i = 0; i < n; i++) {
            Point a = points.get(i);
            Point b = points.get((i+1)%n);
            double term = (a.x * b.y - b.x * a.y) - compensation;
            double newSum = sum + term;
            compensation = (newSum - sum) - term;
            sum = newSum;
        }
        return Math.abs(sum) / 2.0;
    }

    // 动态分带误差估算
    private static double estimateZoneDeviation(List<Coordinate> coords, String crsCode) {
        try {
            Coordinate center = coords.get(coords.size()/2);
            List<Point> points = convertToUTM(coords, crsCode);
            Point p = points.get(points.size()/2);
            
            // 反向投影验证坐标偏移量
            ProjCoordinate reversed = reverseProjection(p.x, p.y, crsCode);
            double dx = reversed.x - center.lon;
            double dy = reversed.y - center.lat;
            return Math.sqrt(dx*dx + dy*dy); // 偏移量作为误差参考
        } catch (Exception e) {
            return Double.MAX_VALUE;
        }
    }

    private static ProjCoordinate reverseProjection(double x, double y, String srcCRS) {
        try {
            CoordinateTransform transform = new CoordinateTransformFactory()
                .createTransform(crsFactory.createFromName(srcCRS), crsFactory.createFromName("EPSG:4326"));
            ProjCoordinate dest = new ProjCoordinate();
            transform.transform(new ProjCoordinate(x, y), dest);
            return dest;
        } catch (Exception e) {
            throw new RuntimeException("反向投影失败: " + e.getMessage());
        }
    }

    // 其他工具方法保持不变，仅优化关键路径
    private static void enforcePolygonClosure(List<Point> points) {
        if (!points.isEmpty() && !points.get(0).equals(points.get(points.size()-1))) {
            points.add(points.get(0));
        }
    }

    private static int calculateUTMZone(double longitude) {
        return (int) Math.floor((longitude + 180) / 6) + 1;
    }

    private static String buildCRSCode(int zone, double lat) {
        return "EPSG:" + (lat >= 0 ? "326" : "327") + (zone > 0 ? zone : 60 + zone); // 处理负区号
    }

    private static void validateCoordinates(List<Coordinate> coordinates) {
        if (coordinates.size() < 3) {
            throw new IllegalArgumentException("至少需要3个点构成多边形");
        }

        // 可选：校验坐标范围（经度-180~180，纬度-90~90）
        for (Coordinate coord : coordinates) {
            if (coord.lon < -180 || coord.lon > 180 ||
                    coord.lat < -90 || coord.lat > 90) {
                throw new IllegalArgumentException(
                        String.format("非法坐标值：[%.6f, %.6f]", coord.lon, coord.lat)
                );
            }
        }
    }

    private static List<Point> convertToUTM(
            List<Coordinate> coordinates,
            String targetCRSCode
    ) {
        CRSFactory crsFactory = new CRSFactory();
        List<Point> utmPoints = new ArrayList<>();

        try {
            // 1. 定义源坐标系（WGS84）和目标坐标系（如EPSG:32650）
            CoordinateReferenceSystem srcCrs = crsFactory.createFromName("EPSG:4326");
            CoordinateReferenceSystem targetCrs = crsFactory.createFromName(targetCRSCode);
            CoordinateTransform transform = new CoordinateTransformFactory()
                    .createTransform(srcCrs, targetCrs);

            // 2. 逐点转换
            for (Coordinate coord : coordinates) {
                ProjCoordinate srcCoord = new ProjCoordinate(coord.lon, coord.lat);
                ProjCoordinate destCoord = new ProjCoordinate();
                transform.transform(srcCoord, destCoord);
                utmPoints.add(new Point(destCoord.x, destCoord.y));
            }
        } catch (InvalidValueException e) {
            throw new IllegalArgumentException("坐标系转换失败: " + e.getMessage());
        }
        return utmPoints;
    }

    // 辅助类
    static class Coordinate {
        double lon;
        double lat;

        Coordinate(double lon, double lat) {
            this.lon = lon;
            this.lat = lat;
        }
    }

    static class Point {
        double x;
        double y;

        Point(double x, double y) {
            this.x = x;
            this.y = y;
        }
    }
}
